package com.mebs.model.logistics;

import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.OrderBy;
import javax.persistence.PrimaryKeyJoinColumn;
import javax.persistence.Table;
import javax.persistence.Transient;

import org.jt.jet.annotation.ArgumentType;
import org.jt.jet.annotation.Field;
import org.jt.jet.util.JetUtils;

import com.mebs.model.Address;
import com.mebs.model.Agent;
import com.mebs.model.AttachedFile;
import com.mebs.model.CurrencyName;
import com.mebs.model.DocsStatus;
import com.mebs.model.VolumeUnit;
import com.mebs.model.WeightUnit;
import com.mebs.model.attachments.AttachableObject;

/**
 * Created by Sergey Prasolov, sap@jensentechnologies.com
 * <p/>
 * Date: Feb 22, 2006, 9:00:28 PM
 */
@Entity()
@Inheritance(strategy = InheritanceType.JOINED)
@Table(name = "TRANSPORTATION")
@PrimaryKeyJoinColumn(name = "Id")
public abstract class Transportation extends LogisticsOperation implements
		AttachableObject {
	@ManyToOne(cascade = CascadeType.PERSIST, optional = false, fetch = FetchType.LAZY)
	@JoinColumn(name = "OriginAddressId")
	private Address origin;

	@Column(name = "OriginType", nullable = true)
	private Integer originType;

	@Column(name = "StartDate", nullable = false)
	private Date startDate;

	@ManyToOne(cascade = CascadeType.PERSIST, optional = false, fetch = FetchType.LAZY)
	@JoinColumn(name = "DestinationAddressId")
	private Address destination;

	@Column(name = "DestinationType", nullable = true)
	private Integer destinationType;

	@Column(name = "FinishDate", nullable = true)
	private Date finishDate;

	@Column(name = "ShipmentValue", nullable = true)
	private Double shipmentValue;

	@Column(name = "ShipmentLot", nullable = true)
	private String shipmentLot;

	@ManyToOne(optional = true, fetch = FetchType.LAZY)
	@JoinColumn(name = "ShipmentValueCurrencyId")
	private CurrencyName shipmentValueCurrency;

	@Column(name = "InsuranceValue", nullable = true)
	private Double insuranceValue;

	@Column(name = "InsuranceC", nullable = true)
	private Double insuranceC;

	@Column(name = "InsuranceI", nullable = true)
	private Double insuranceI;

	@Column(name = "InsuranceF", nullable = true)
	private Double insuranceF;

	@Column(name = "InsurancePercent", nullable = true)
	private Double insurancePercent;

	@ManyToOne(optional = true, fetch = FetchType.LAZY)
	@JoinColumn(name = "InsuranceCurrencyId")
	private CurrencyName insuranceCurrency;

	@Column(name = "Weight", nullable = true)
	private Double weight;

	@Column(name = "WeightUnit", nullable = true)
	private Integer weightUnit;

	@Column(name = "Volume", nullable = true)
	private Double volume;

	@Column(name = "VolumeUnit", nullable = true)
	private Integer volumeUnit;

	@Column(name = "NotifyUsers", nullable = true, length = 1024)
	private String notifyUsers;

	@Column(name = "Hazmat", nullable = true)
	private Boolean hazmat;

	@Column(name = "Crated", nullable = true)
	private Boolean crated;

	@Column(name = "Consolidated", nullable = true)
	private Boolean consolidated;

	@Column(name = "PreAlert", nullable = true)
	private Date preAlert;

	@Column(name = "AlertedConsignee", nullable = true)
	private Boolean alertedConsignee;

	@Column(name = "AlertedAgent", nullable = true)
	private Boolean alertedAgent;

	@Column(name = "AwbOblPro", length = 50)
	private String awbOblPro;

	@Column(name = "OBLAWBReceived", nullable = true)
	private Date obl_awb_received;

	@Column(name = "Carrier", nullable = true)
	private String carrier;

	@Column(name = "OBLAWBSentToClient", nullable = true)
	private Date obl_awb_sent_to_client;

	@Column(name = "DocsSentToAgent", nullable = true)
	private Date docs_sent_to_agent;

	@Column(name = "Priority", nullable = true)
	private Integer priority;

	@Column(name = "LoadDate", nullable = true)
	private Date loadDate;

	@Column(name = "Quoted", nullable = true)
	private Date quoted;

	@Column(name = "QuoteAmount", nullable = true)
	private Double quoteAmount;

	@Column(name = "QuoteNotes", nullable = true, length = 1054)
	private String quoteNotes;

	@Column(name = "CargoReceived", nullable = true)
	private Date cargoReceived;

	@Column(name = "GeneralDescription", nullable = true, length = 1054)
	private String generalDescription;

	@Column(name = "DocsStatus", nullable = true)
	private Integer docsStatus;

	@Column(name = "SequenceNumber", nullable = true, length = 64)
	private String sequenceNumber;

	@ManyToOne(optional = true)
	@JoinColumn(name = "FileId")
	private AttachedFile file;

	@ManyToOne(optional = true, fetch = FetchType.LAZY)
	@JoinColumn(name = "AgentId")
	private Agent agent;

	@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
	@JoinColumn(name = "TransportationId")
	@OrderBy("indexNumber")
	private List<Leg> legs;

	@ManyToOne(cascade = CascadeType.PERSIST, optional = true, fetch = FetchType.LAZY, targetEntity = Notify.class)
    @JoinColumn(name = "NotifyId")
    private Notify notify;
	
	@ManyToOne(cascade = CascadeType.PERSIST, optional = true, fetch = FetchType.LAZY, targetEntity = Consignee.class)
    @JoinColumn(name = "ConsigneeId")
    private Consignee consignee;
	
	@Column(name = "Seal", nullable = true)
	private String seal;
	
	@Column(name = "Container", nullable = true)
	private String container;
	
	@Column(name = "FinalDestination", nullable = true)
	private String finalDestination;
	
	@Column(name = "CargoDescription", nullable = true)
	private String cargoDescription;
	
	@Column(name = "last214GenerateTime", nullable = true)
	private Date last214GenerateTime;
	
	@Column(name = "last214Number", nullable = true)
	private Integer last214Number;

	/**
	 * Constructors
	 */
	public Transportation() {
	}

	/**
	 * Getters-setters
	 */

	public Address getOrigin() {
		return origin;
	}

	public void setOrigin(Address origin) {
		this.origin = origin;
	}

	public Integer getOriginType() {
		return originType;
	}

	public void setOriginType(Integer originType) {
		this.originType = originType;
	}

	public Date getStartDate() {
		return startDate;
	}

	public void setStartDate(Date startDate) {
		this.startDate = startDate;
	}

	public Address getDestination() {
		return destination;
	}

	public void setDestination(Address destination) {
		this.destination = destination;
	}

	public Integer getDestinationType() {
		return destinationType;
	}

	public void setDestinationType(Integer destinationType) {
		this.destinationType = destinationType;
	}

	public Date getFinishDate() {
		return finishDate;
	}

	public void setFinishDate(Date finishDate) {
		this.finishDate = finishDate;
	}

	public Double getShipmentValue() {
		return shipmentValue;
	}

	public void setShipmentValue(Double shipmentValue) {
		this.shipmentValue = shipmentValue;
	}

	public String getShipmentLot() {
		return shipmentLot;
	}

	public void setShipmentLot(String shipmentLot) {
		this.shipmentLot = shipmentLot;
	}

	public CurrencyName getShipmentValueCurrency() {
		return shipmentValueCurrency;
	}

	public void setShipmentValueCurrency(CurrencyName shipmentValueCurrency) {
		this.shipmentValueCurrency = shipmentValueCurrency;
	}

	public Double getInsuranceValue() {
		return insuranceValue;
	}

	public void setInsuranceValue(Double insuranceValue) {
		this.insuranceValue = insuranceValue;
	}

	public Double getInsuranceC() {
		return insuranceC;
	}

	public void setInsuranceC(Double insuranceC) {
		this.insuranceC = insuranceC;
	}

	public Double getInsuranceI() {
		return insuranceI;
	}

	public void setInsuranceI(Double insuranceI) {
		this.insuranceI = insuranceI;
	}

	public Double getInsuranceF() {
		return insuranceF;
	}

	public void setInsuranceF(Double insuranceF) {
		this.insuranceF = insuranceF;
	}

	public Double getInsurancePercent() {
		return insurancePercent;
	}

	public void setInsurancePercent(Double insurancePercent) {
		this.insurancePercent = insurancePercent;
	}

	public CurrencyName getInsuranceCurrency() {
		return insuranceCurrency;
	}

	public void setInsuranceCurrency(CurrencyName insuranceCurrency) {
		this.insuranceCurrency = insuranceCurrency;
	}

	public Double getWeight() {
		return weight;
	}

	public void setWeight(Double weight) {
		this.weight = weight;
	}

	public Integer getWeightUnit() {
		return weightUnit;
	}

	public void setWeightUnit(Integer weightUnit) {
		this.weightUnit = weightUnit;
	}

	public Double getVolume() {
		return volume;
	}

	public void setVolume(Double volume) {
		this.volume = volume;
	}

	public Integer getVolumeUnit() {
		return volumeUnit;
	}

	public void setVolumeUnit(Integer volumeUnit) {
		this.volumeUnit = volumeUnit;
	}

	public String getNotifyUsers() {
		return notifyUsers;
	}

	public void setNotifyUsers(String notifyUsers) {
		this.notifyUsers = notifyUsers;
	}

	public Boolean getCrated() {
		if (crated == null) {
			return false;
		}
		return crated;
	}

	public void setCrated(Boolean crated) {
		this.crated = crated;
	}

	public Boolean getConsolidated() {
		if (consolidated == null) {
			return false;
		}
		return consolidated;
	}

	public void setConsolidated(Boolean consolidated) {
		this.consolidated = consolidated;
	}

	public Boolean getHazmat() {
		if (hazmat == null) {
			return false;
		}
		return hazmat;
	}

	public void setHazmat(Boolean hazmat) {
		this.hazmat = hazmat;
	}

	public Date getPreAlert() {
		return preAlert;
	}

	public void setPreAlert(Date preAlert) {
		this.preAlert = preAlert;
	}

	public Boolean getAlertedConsignee() {
		if (alertedConsignee == null) {
			return false;
		}
		return alertedConsignee;
	}

	public void setAlertedConsignee(Boolean alertedConsignee) {
		this.alertedConsignee = alertedConsignee;
	}

	public Boolean getAlertedAgent() {
		if (alertedAgent == null) {
			return false;
		}
		return alertedAgent;
	}

	public void setAlertedAgent(Boolean alertedAgent) {
		this.alertedAgent = alertedAgent;
	}

	public String getAwbOblPro() {
		return awbOblPro;
	}

	public void setAwbOblPro(String awbOblPro) {
		this.awbOblPro = awbOblPro;
	}

	public List<Leg> getLegs() {
		return legs;
	}

	public void setLegs(List<Leg> legs) {
		this.legs = legs;
	}

	public Date getObl_awb_received() {
		return obl_awb_received;
	}

	public void setObl_awb_received(Date obl_awb_received) {
		this.obl_awb_received = obl_awb_received;
	}

	public String getCarrier() {
		return carrier;
	}

	public void setCarrier(String carrier) {
		this.carrier = carrier;
	}

	public Date getObl_awb_sent_to_client() {
		return obl_awb_sent_to_client;
	}

	public void setObl_awb_sent_to_client(Date obl_awb_sent_to_client) {
		this.obl_awb_sent_to_client = obl_awb_sent_to_client;
	}

	public Date getDocs_sent_to_agent() {
		return docs_sent_to_agent;
	}

	public void setDocs_sent_to_agent(Date docs_sent_to_agent) {
		this.docs_sent_to_agent = docs_sent_to_agent;
	}

	public Integer getPriority() {
		return priority == null ? 0 : priority;
	}

	public void setPriority(Integer priority) {
		this.priority = priority;
	}

	public Date getLoadDate() {
		return loadDate;
	}

	public void setLoadDate(Date loadDate) {
		this.loadDate = loadDate;
	}

	public Date getQuoted() {
		return quoted;
	}

	public void setQuoted(Date quoted) {
		this.quoted = quoted;
	}

	public Double getQuoteAmount() {
		return quoteAmount;
	}

	public void setQuoteAmount(Double quoteAmount) {
		this.quoteAmount = quoteAmount;
	}

	public String getQuoteNotes() {
		return quoteNotes;
	}

	public void setQuoteNotes(String quoteNotes) {
		this.quoteNotes = quoteNotes;
	}

	public Date getCargoReceived() {
		return cargoReceived;
	}

	public void setCargoReceived(Date cargoReceived) {
		this.cargoReceived = cargoReceived;
	}

	public String getGeneralDescription() {
		return generalDescription;
	}

	public void setGeneralDescription(String generalDescription) {
		this.generalDescription = generalDescription;
	}

	public Integer getDocsStatus() {
		return docsStatus;
	}

	public void setDocsStatus(Integer docsStatus) {
		this.docsStatus = docsStatus;
	}

	public Agent getAgent() {
		return agent;
	}

	public void setAgent(Agent agent) {
		this.agent = agent;
	}

	public Notify getNotify() {
		return notify;
	}

	public void setNotify(Notify notify) {
		this.notify = notify;
	}
	
	public Consignee getConsignee() {
		return consignee;
	}

	public void setConsignee(Consignee consignee) {
		this.consignee = consignee;
	}

	public String getSeal() {
		return seal;
	}

	public void setSeal(String seal) {
		this.seal = seal;
	}

	public String getContainer() {
		return container;
	}

	public void setContainer(String container) {
		this.container = container;
	}

	public String getFinalDestination() {
		return finalDestination;
	}

	public void setFinalDestination(String finalDestination) {
		this.finalDestination = finalDestination;
	}

	public String getCargoDescription() {
		return cargoDescription;
	}

	public void setCargoDescription(String cargoDescription) {
		this.cargoDescription = cargoDescription;
	}

	public Date getLast214GenerateTime() {
		return last214GenerateTime;
	}

	public void setLast214GenerateTime(Date last214GenerateTime) {
		this.last214GenerateTime = last214GenerateTime;
	}

	public Integer getLast214Number() {
		return last214Number;
	}

	public void setLast214Number(Integer last214Number) {
		this.last214Number = last214Number;
	}

	@Transient
	public Date getETAMax() {
		long timeTemp = Long.MIN_VALUE;
		if (this.legs != null) {
			for (Leg l : getLegs()) {
				if (l.getEta() != null && l.getEta().getTime() > timeTemp) {
					timeTemp = l.getEta().getTime();
				}
			}
		}

		return timeTemp == Long.MIN_VALUE ? null : new Date(timeTemp);
	}

	public Date getATDMin() {
		long timeTemp = Long.MAX_VALUE;
		if (this.legs != null) {
			for (Leg l : getLegs()) {
				if (l.getAtd() != null && l.getAtd().getTime() < timeTemp) {
					timeTemp = l.getAtd().getTime();
				}
			}
		}

		return timeTemp == Long.MAX_VALUE ? null : new Date(timeTemp);
	}
	
	@Transient
	public Date getETDMin() {
		long timeTemp = Long.MAX_VALUE;
		if (this.legs != null) {
			for (Leg l : getLegs()) {
				if (l.getEtd() != null && l.getEtd().getTime() < timeTemp) {
					timeTemp = l.getEtd().getTime();
				}
			}
		}

		return timeTemp == Long.MAX_VALUE ? null : new Date(timeTemp);
	}

	@Transient
	public Leg getFirstLeg() {
		if (legs != null && !legs.isEmpty()) {
			return legs.get(0);
		} else {
			return null;
		}
	}
	
	/**
	 * just the last Leg in the legs collection.
	 * Legacy method, left here to pevent breaking the old MEBS code 
	 * @return
	 */
	@Transient
	public Leg getLastLeg() {
		if (legs != null && !legs.isEmpty()) {
			return legs.get(legs.size() - 1);
		} else {
			return null;
		}
	}
	
	/**
	 * the leg that has the "lastLeg" flag set
	 * @return
	 */
	@Transient
	public Leg getFinalLeg() {
		for (Leg l : legs) {
			if (l.getLastLeg() != null && l.getLastLeg()) {
				return l;
			}
		}
		return null;
	}
	
	@Transient
	public Date getAtdOfFirstLeg() {
		return getFirstLeg() != null ? getFirstLeg().getAtd() : null;
	}

	@Transient
	public Date getAtaOfLastLeg() {
		return getLastLeg() != null ? getLastLeg().getAta() : null;
	}
	
	@Transient
	protected boolean isDeletable() {
		return (JetUtils.isEmpty(this.legs));
	}

	public String getSequenceNumber() {
		if (sequenceNumber == null) {
			if (getSequence() != null) {
				Calendar c = new GregorianCalendar();
				c.setTime(getStartDate());
				sequenceNumber = String.format("%s-%02d%02d-%05d",
						getCompany().getTicker() == null ? "XXX" : getCompany()
								.getTicker(), c.get(Calendar.MONTH) + 1, c
								.get(Calendar.YEAR) % 100, getSequence());
			}
		}
		return sequenceNumber;
	}

	public void setSequenceNumber(String sequenceNumber) {
		this.sequenceNumber = sequenceNumber;
	}

	// @Transient
	// public String getSequenceNumber()
	// {
	// Calendar c = new GregorianCalendar();
	// c.setTime(getStartDate());
	// return String.format("%s-%s-%05d-%02d%02d%02d",
	// "TR",
	// getCompany().getTicker() == null ? "XXX" : getCompany().getTicker(),
	// getSequence(),
	// c.get(Calendar.YEAR) % 100,
	// c.get(Calendar.MONTH) + 1,
	// c.get(Calendar.DAY_OF_MONTH));
	// }

	@Override
	public String toString() {
		return (this.getSequence() != null) ? this.getSequence().toString()
				: "";
	}

	@Transient
	public String getWeightUnitString() {
		return this.weightUnit != null ? WeightUnit.getObjectByType(
				this.weightUnit).getName() : "";
	}

	@Transient
	public String getVolumeUnitString() {
		return volumeUnit != null ? VolumeUnit.getObjectByType(volumeUnit)
				.getName() : "";
	}

	@Transient
	public String getOriginTypeString() {
		return getTypeString(this.originType);
	}

	@Transient
	public String getDestinationTypeString() {
		return getTypeString(this.destinationType);
	}

	@Transient
	private String getTypeString(Integer type) {
		if (type == null) {
			return "";
		}

		return LocationType.getObjectByType(type).getObjectName();
	}

	@Transient
	public String getDocsStatusString() {
		return docsStatus == null ? "" : DocsStatus.getObjectByType(docsStatus)
				.getObjectName();
	}

	public AttachedFile getFile() {
		return file;
	}

	public void setFile(AttachedFile file) {
		this.file = file;
	}
	
	@Transient
	public Leg getZeroLeg() {
		for (Leg l : getLegs()) {
			if (l.getIndexNumber() == 0) {
				return l;
			}
		}
		return null;
	}
}
